import cStringIO

class Closure(object):
    def __init__(self, proto, frame):
        self.proto = proto 
        self.upvars = []
        d = {}
        for type, index in proto.upvars:
            if type == "local":
                # chain upvars referring a same frame index.
                if index in d:
                    upvar = Upvar(frame, index, Upvar.INACTIVE)
                    d[index].next  = upvar
                    d[index] = upvar
                else:
                    upvar = Upvar(frame, index, Upvar.OPEN)
                    d[index] = upvar
                self.upvars.append(upvar)
            elif type == "varg":
                self.upvars.append(UpvarForVarg(frame))
            elif type == "up":
                self.upvars.append(frame.upvars[index])
            else:
                assert 0
        
class UpvarForVarg(object):
    def __init__(self, frame):
        self.frame = frame
        self.closed = False

    def get(self):
        if self.closed:
            return self.value
        else:
            return self.frame.varargs

    def set(self, value):
        if self.closed:
            self.value = value
        else:
            self.frame.varargs = value

    def close(self):
        if not self.closed:
            self.value = self.frame.varargs
            del self.frame
            self.closed = True

class Upvar(object):
    CLOSED = 0
    OPEN = 1
    INACTIVE = 2

    def __init__(self, frame, index, stat):
        self.frame = frame
        self.index = index
        self.stat = stat
        self.next = None
    
    def get(self):
        assert self.stat != Upvar.INACTIVE
        if self.stat == Upvar.CLOSED:
            return self.value
        else:
            return self.frame.localvars[self.index]

    def set(self, value):
        assert self.stat != Upvar.INACTIVE
        if self.stat == Upvar.CLOSED:
            self.value = value
        else:
            self.frame.localvars[self.index] = value

    def close(self):
        assert self.stat == Upvar.OPEN
        self.value = self.frame.localvars[self.index]
        if self.next != None:
            # activet next upvar with the same index on frame.
            self.next.stat = Upvar.OPEN 
            del self.next
            del self.frame
        self.stat == Upvar.CLOSED

def build_list(seq):
    ret = None
    for i in reversed(seq):
        ret = cons(i,ret)
    return ret

class LinkList(object):
    def __init__(self, v, next=None):
        self.v = v
        self.next = next

    def cons(self, v):
        new = LinkList(v)
        new.next = self
        return new

    def car(self):
        return self.v

    def cdr(self):
        return self.next 

    def __str__(self):
        n = self
        sb = cStringIO.StringIO()
        sb.write("(%s" % self.v)
        try:
            while n.next:
                n = n.next
                sb.write(" %s" % str(n.v))
        except AttributeError:
            sb.write(" . %s" % n)
        sb.write(")")
        return sb.getvalue()

def cons(x, y):
    if not isinstance(y, LinkList):
        return LinkList(x, next=y)
    else:
        return y.cons(x)

def car(x):
    if not isinstance(x, LinkList):
        raise ValueError("Unsupported operation car")
    return x.car()

def cdr(x):
    if not isinstance(x, LinkList):
        raise ValueError("Unsupported operation cdr")
    return x.cdr()
